package com.gs.netty.support.handler.method;


import com.gs.netty.core.annotation.Interceptor;
import com.gs.netty.core.annotation.Order;
import com.gs.netty.core.common.Ordered;
import com.gs.netty.core.handler.HandlerExecutionChain;
import com.gs.netty.core.handler.HandlerMapping;
import com.gs.netty.core.handler.method.HandlerMethod;
import com.gs.netty.core.interceptor.HandlerInterceptor;

import com.gs.netty.support.MappedInterceptor;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.MethodIntrospector;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.util.ClassUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 抽象处理映射器类
 * @param <Request> 请求对象
 * @param <A> 注解信息
 */

public abstract class AbstractHandlerMapping<Request,Response,A> implements HandlerMapping<Request>, InitializingBean , ApplicationContextAware {

    private static final String SCOPED_TARGET_NAME_PREFIX = "scopedTarget.";

    protected final Log logger = LogFactory.getLog(getClass());

    /**
     * 方法处理器容器
     */
    protected final MappingRegistry mappingRegistry = new MappingRegistry();
    /**
     * 拦截器
     */
    private final List<HandlerInterceptor> interceptors = new ArrayList<>();


    private ApplicationContext applicationContext;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    public ApplicationContext getApplicationContext() {
        return applicationContext;
    }


    public Collection<A> getHandlerInfos() {
        return (Collection<A>) mappingRegistry.pathLookup.values();
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        // 初始化方法处理器
        initHandlerMethods();
        // 初始化拦截器
        initInterceptors();
    }

    private void initInterceptors() {
        // 找到所有拦截器
        Collection<HandlerInterceptor> handlerInterceptors = BeanFactoryUtils.beansOfTypeIncludingAncestors(
                applicationContext, HandlerInterceptor.class, true, false).values();

        if (!handlerInterceptors.isEmpty()) {
            TreeMap<Integer, HandlerInterceptor> sortMap = new TreeMap<>();

            for (HandlerInterceptor handlerInterceptor : handlerInterceptors) {
                // 获取注解
                Interceptor annotation = AnnotatedElementUtils.getMergedAnnotation(handlerInterceptor.getClass(), Interceptor.class);
                HandlerInterceptor interceptor =null;
                // 排序的值
                int order = Integer.MAX_VALUE;

                if (annotation != null) {
                    // 证明是有条件的拦截器
                    interceptor = new MappedInterceptor(handlerInterceptor, annotation);
                    order = annotation.order();
                }else if (handlerInterceptor instanceof Ordered) {// 是否实现这个接口
                    interceptor = handlerInterceptor;
                    order  = ((Ordered) handlerInterceptor).getOrder();
                }
                else {
                    interceptor = handlerInterceptor;
                    Order OrderAnnotation = AnnotatedElementUtils.getMergedAnnotation(handlerInterceptor.getClass(), Order.class);
                    if (OrderAnnotation != null) {
                        order = OrderAnnotation.value();
                    }
                }
                sortMap.put(order, interceptor);
            }
            // 添加
            interceptors.addAll(sortMap.values());
        }
    }


    protected String[] getCandidateBeanNames() {
        return applicationContext.getBeanNamesForType(Object.class);
    }

    protected void initHandlerMethods() {
        for (String beanName : getCandidateBeanNames()) {
            if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
                processCandidateBean(beanName);
            }
        }
    }

    protected void processCandidateBean(String beanName) {
        Class<?> beanType = null;
        try {
            beanType = getApplicationContext().getType(beanName);
        }
        catch (Throwable ex) {
            // An unresolvable bean type, probably from a lazy bean - let's ignore it.
            if (logger.isTraceEnabled()) {
                logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
            }
        }
        if (beanType != null && isHandler(beanType)) {
            detectHandlerMethods(beanName);
        }
    }

    protected void detectHandlerMethods(Object handler) {
        Class<?> handlerType = (handler instanceof String ?
                getApplicationContext().getType((String) handler) : handler.getClass());

        if (handlerType != null) {
            Class<?> userType = ClassUtils.getUserClass(handlerType);
            /**
             *  key：方法
             *  value：注解信息
             */
            Map<Method, A> methods = MethodIntrospector.selectMethods(userType,
                    (MethodIntrospector.MetadataLookup<A>) method -> {
                        try {

                            return (A) getMappingForMethod(method, userType);
                        }
                        catch (Throwable ex) {
                            throw new IllegalStateException("Invalid mapping on handler class [" +
                                    userType.getName() + "]: " + method, ex);
                        }
                    });

            methods.forEach((method, mapping) -> {

                Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);

                registerHandlerMethod(handler, invocableMethod, mapping);
            });
        }
    }

    @Override
    public HandlerExecutionChain getHandler(Request request) throws Exception {

        HandlerMethod handlerMethod = getHandlerInternal(request);
        // 找不到处理器
        if (handlerMethod == null) {
            // 获取一下默认处理器
            handlerMethod = getDefaultHandler(request);
        }
        if (handlerMethod == null) {
            return null;
        }
        return getHandlerExecutionChain(handlerMethod,request);
    }

    protected abstract HandlerMethod getDefaultHandler(Request request);


    protected HandlerExecutionChain getHandlerExecutionChain(Object handler, Request request) {
        HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
                (HandlerExecutionChain) handler : new HandlerExecutionChain<Request, Response>(handler));

        if (!interceptors.isEmpty()){
            for (HandlerInterceptor interceptor : interceptors) {
                if (interceptor instanceof MappedInterceptor) {
                    MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
                    // 匹配是否符合条件
                    if (mappedInterceptor.matches(request)) {

                        chain.addInterceptor(mappedInterceptor.getHandlerInterceptor());
                    }
                }else {
                    chain.addInterceptor(interceptor);
                }
            }
        }
        return chain;
    }

    protected abstract HandlerMethod getHandlerInternal(Request request);

    protected  HandlerMethod doGetHandler(String lookupCommand,Request request){
        this.mappingRegistry.acquireReadLock();
        try {
            // 找到处理器
            HandlerMethod handlerMethod = lookupHandlerMethod(lookupCommand, request);
            return handlerMethod!=null ? handlerMethod.createWithResolvedBean() : null;
        }
        finally {
            this.mappingRegistry.releaseReadLock();
        }
    }

    protected abstract Set<String> getDirectPaths(A mapping);

    protected HandlerMethod lookupHandlerMethod(String lookupCommand, Request request) {

        // 根据指令找到方法上的注解
        List<A> mappingsByDirectPath = this.mappingRegistry.getMappingsByDirectPath(lookupCommand);

        if (mappingsByDirectPath == null) {
            return null;
        }

        A a = mappingsByDirectPath.get(0);

        // 获取方法处理器
        return this.mappingRegistry.registry.get(a);
    }



    /**
     *
     * @param handler
     * @param invocableMethod
     * @param mapping
     */
    private void registerHandlerMethod(Object handler, Method invocableMethod, A mapping) {

        // 注册
        mappingRegistry.register(mapping,handler,invocableMethod);
    }

    protected abstract A getMappingForMethod(Method method, Class<?> handlerType);





    protected abstract boolean isHandler(Class<?> beanType);


    class MappingRegistry {

        /**
         * key：程序指令
         * value：注解信息
         */

        private final MultiValueMap<String, A> pathLookup = new LinkedMultiValueMap<>();

        /**
         * key：注解信息
         * value：方法处理器
         */
        private final Map<A, HandlerMethod> registry = new HashMap<>();

        private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

            public void register(A mapping, Object handler, Method method) {
                this.readWriteLock.writeLock().lock();

                try {
                    HandlerMethod handlerMethod = createHandlerMethod(handler, method);
                    // 校验是否存在相同的
                    validateMethodMapping(handlerMethod, mapping);
                    // 获取直接地址映射
                    Set<String> directPaths = AbstractHandlerMapping.this.getDirectPaths(mapping);
                    for (String path : directPaths) {
                        this.pathLookup.add(path, mapping);
                    }



                    // 添加
                    this.registry.put(mapping, handlerMethod);
                }
                finally {
                    this.readWriteLock.writeLock().unlock();
                }
            }

        private void validateMethodMapping(HandlerMethod handlerMethod, A mapping) {
            HandlerMethod handler= this.registry.get(mapping);
            HandlerMethod existingHandlerMethod = (handler != null ? handler : null);
            if (existingHandlerMethod != null && !existingHandlerMethod.equals(handlerMethod)) {
                throw new IllegalStateException(
                        "Ambiguous mapping. Cannot map '" + handlerMethod.getBean() + "' method \n" +
                                handlerMethod + "\nto " + mapping + ": There is already '" +
                                existingHandlerMethod.getBean() + "' bean method\n" + existingHandlerMethod + " mapped.");
            }
        }
        public List<A> getMappingsByDirectPath(String lookupCommand) {
            return this.pathLookup.get(lookupCommand);
        }

        public  void acquireReadLock() {
            this.readWriteLock.readLock().lock();
        }

        public void releaseReadLock() {
            this.readWriteLock.readLock().unlock();
        }
    }

    private HandlerMethod createHandlerMethod(Object handler, Method method) {
        return new HandlerMethod((String) handler, method,getApplicationContext().getAutowireCapableBeanFactory());
    }


}
